   
#include "meshWithSkeleton.h"

#include "polyhedronBuilder.h"
#include "meshLoader.h"
//~ #include <CGAL/Polyhedron_3.h>
//~ #include <CGAL/IO/Polyhedron_iostream.h>

#include <iomanip>

//~ namespace MLS {
   
MeshWithSkeleton::MeshWithSkeleton() 
   : Mesh() {
   
   skeleton = 0;
   i_skeleton = 0;
   lfalpha = 0.0;
   largest_dvj = 0.0;
}

MeshWithSkeleton::MeshWithSkeleton(const char* filename) 
   : Mesh(filename) {
   
   skeleton = 0;
   i_skeleton = 0;
   lfalpha = 0.0;
   largest_dvj = 0.0;
}
   
MeshWithSkeleton::~MeshWithSkeleton() {
   
}

MeshWithSkeleton* MeshWithSkeleton::load_off(const char *filename) {
   
   std::fstream off_file(filename, std::ios_base::in);
   if (off_file) {
      MeshWithSkeleton *p = new MeshWithSkeleton(filename);
      return p;
   }
   return 0; 
}

MeshWithSkeleton* MeshWithSkeleton::load_ply(const char *filename) {
   
   std::fstream ply_file(filename, std::ios_base::in);
   if (ply_file) {
      LoaderPLY<MeshWithSkeleton::HalfedgeDS> builder(filename);
      MeshWithSkeleton *p = new MeshWithSkeleton;
      p->delegate(builder);
      if (not p->is_valid()) return 0;
      
      p->auxiliarData();
      
      assert(p->nvertices() == (unsigned int)builder.nverts);
      Vertex_iterator vi = p->vertices_begin();
      for (unsigned int i=0; i<p->nvertices(); ++i, ++vi) {
         const Number &r = builder.vlist[i]->red/255.0;
         const Number &g = builder.vlist[i]->green/255.0;
         const Number &b = builder.vlist[i]->blue/255.0;
         vi->color(Vector3(r,g,b));
      }
      return p;
   }
   return 0;
}

void MeshWithSkeleton::loadSkinningOnJoints(const char* filename) {
   
   /// new version
   if (filename==0) return;
   //~ if (faces==0)   return;
   if (_nvertices==0 or _nfaces==0)               return;
   if (skeleton==0)                               return;
      
   string skelformat; 
   unsigned int nofvertices; unsigned int njoints;
   std::ifstream filein(filename);
   if (not filein.is_open()) {
      std::cout<<"I/O error: grrr the skinning file doesn't exist"<<std::endl;
      return;
   }
   filein>>skelformat;
   assert(skelformat=="SKINONJOINTS");
   filein>>nofvertices>>njoints;
   
   assert(this->nvertices() == nofvertices);
   
   // Load the data
   Number w; Number t;
   Vertex_iterator vi = this->vertices_begin();
   while (vi != this->vertices_end()) {
      Number W=0; int ijoint=-1;
      for (unsigned int i=0; i<njoints; ++i) {
         filein>>w;
         if (w<1e-8) w=0.0;
         
         vi->jointsweights.push_back(w);
         if (w>W) { W = w; ijoint = i; }
         
         // Compute the closest point
         const Point3 &vp = vi->point();
         Number d = skeleton->getDistance(&vp[0], i, t);
         vi->ts.push_back(t); // the closest point
      }
      assert(ijoint != -1);
      vi->rigging_group = ijoint;
      const Point3 &vp = vi->point();
      vi->dClosestJoint = skeleton->getDistance(&vp[0], ijoint, t);
      cout<<"vi->dClosestJoint: "<<vi->dClosestJoint<<endl;
      ++vi;
   }
   
   //~ std::cout<<"number of bones(): "<<this->nBones()<<endl;
   std::cout<<"number of joints : "<<njoints<<endl;
   
   computeJointsxVertex();
   
}
   
void MeshWithSkeleton::loadSkinningOnBones(const char* filename) {
   
   /// new version
   if (filename==0) return;
   //~ if (faces==0)   return;
   if (_nvertices==0 or _nfaces==0)               return;
   if (skeleton==0)                               return;
      
   string skelformat; 
   unsigned int nofvertices; unsigned int nbones;
   std::ifstream filein(filename);
   if (not filein.is_open()) {
      std::cout<<"I/O error: grrr the skinning file doesn't exist"<<std::endl;
      return;
   }
   filein>>skelformat;
   assert(skelformat=="SKINONBONES");
   filein>>nofvertices>>nbones;
   
   assert(this->nvertices() == nofvertices);
   
   // Load the data
   Number w; Number t;
   Vertex_iterator vi = this->vertices_begin();
   while (vi != this->vertices_end()) {
      Number W=0; int idbone=0;
      // Introduce the weight for bone 0: that is a bone composed by the joint 0 and -1
      vi->bonesweights.push_back(0.0);
      vi->ts.push_back(0.0); // the closest point
      for (unsigned int i=1; i<=nbones; ++i) {
         filein>>w;
         if (w<1e-8) w=0.0;
         
         vi->bonesweights.push_back(w);
         if (w>W) { W = w; idbone = i; }
         
         // Compute the closest point
         const Point3 &vp = vi->point();
         Number d = skeleton->getDistance(&vp[0], i, t);
         //~ Number d = skeleton->getEllipDistance(vp, i, t);
         vi->ts.push_back(t); // the closest point
      }
      assert(idbone != 0);
      vi->rigging_group = idbone;
      ++vi;
   }
   
   //~ std::cout<<"number of bones(): "<<this->nBones()<<endl;
   std::cout<<"number of bones : "<<nbones<<endl;
   
   //~ computeJointsxVertex();
   computeBonesxVertex();
   
}
   
void MeshWithSkeleton::saveSkinningOnBones(const char* filename) {
   
   /// version using difussion over the mesh
   if (filename==0)                                return;
   //~ if (faces==0)    return;
   if (_nvertices==0 or _nfaces==0)                return;
   if (skeleton == 0)                              return;
      
   std::ofstream fout(filename);
   if (fout) {
      fout<<"SKINONBONES"<<endl;
      fout<<_nvertices<<" "<<this->nBones()-1<<endl;
      Vertex_iterator vi = this->vertices_begin();
      while (vi != this->vertices_end()) { 
         for (unsigned int i=1; i<vi->bonesweights.size(); ++i) {
            const float &w = (*vi).bonesweights[i];
            fout<<setprecision(13)<<w<<" ";
         }
         fout<<endl;
         ++vi;
      }
      fout.close();
   }
}

void MeshWithSkeleton::saveSkinningOnJoints(const char* filename) {
   
   /// version using difussion over the mesh
   if (filename==0)                                return;
   //~ if (faces==0)    return;
   if (_nvertices==0 or _nfaces==0)                return;
   if (skeleton == 0)                              return;
      
   std::ofstream fout(filename);
   if (fout) {
      fout<<"SKINONJOINTS"<<endl;
      fout<<_nvertices<<" "<<this->nJoints()<<endl;
      Vertex_iterator vi = this->vertices_begin();
      while (vi != this->vertices_end()) { 
         for (unsigned int i=0; i<vi->jointsweights.size(); ++i) {
            const float &w = (*vi).jointsweights[i];
            fout<<setprecision(13)<<w<<" ";
         }
         fout<<endl;
         ++vi;
      }
      fout.close();
   }
}

void MeshWithSkeleton::loadSkeleton(const char* filename) {
   
   if (skeleton != 0) delete skeleton;
   skeleton = Skeleton::load(filename);
   i_skeleton = Skeleton::load(filename);
}

void MeshWithSkeleton::saveSkeleton(const char* filename) {
   
   if (skeleton == 0) return;
   skeleton->saveAs(filename);
}

void MeshWithSkeleton::computeJointsxVertex() {
   
   vector<unsigned int>   jointsXvertex;      //< 
   jointsXvertex.resize(20,0);
   
   Vertex_iterator vi = this->vertices_begin();
   while (vi != this->vertices_end()) {
      unsigned int n = std::count(vi->jointsweights.begin(), vi->jointsweights.end(), 0.0);
      if (n<20) jointsXvertex[n]++;
      else      jointsXvertex[19]++;
      ++vi;
   }
   
   cout<<"<::info::> joints X vertex"<<endl;
   for (unsigned int i=0; i<jointsXvertex.size(); ++i) {
      cout<<" <joints numbers>: "<<i<<" :vertices: "
      <<jointsXvertex[i]<<" ::: "<<(jointsXvertex[i]*100.0)/(float)nvertices()<<" % "<<endl;
   }
}

void MeshWithSkeleton::computeBonesxVertex() {
   
   vector<unsigned int>   bonesXvertex;
   bonesXvertex.resize(20,0);
   
   Vertex_iterator vi = this->vertices_begin();
   while (vi != this->vertices_end()) {
      unsigned int n=0;
      for (int i=0; i<vi->bonesweights.size(); ++i) 
         if (vi->bonesweights[i] > 0) ++n;
      
      if (n<20) bonesXvertex[n]++;
      else      bonesXvertex[19]++;
      ++vi;
   }
   
   cout<<"<::info::> bones X vertex"<<endl;
   for (unsigned int i=0; i<bonesXvertex.size(); ++i) {
      cout<<" <bones numbers>: "<<i<<" :vertices: "
      <<bonesXvertex[i]<<" ::: "<<(bonesXvertex[i]*100.0)/(float)nvertices()<<" % "<<endl;
   }
}

void MeshWithSkeleton::rotate(const Point3 &o, const Vector3 &u, Number angle) {
   
   Mesh::rotate(o,u,angle);
   if (skeleton != 0) skeleton->rotate(o,u,angle);
}

void MeshWithSkeleton::translate(const Vector3 &t) {
   
   Mesh::translate(t);
   if (skeleton != 0) skeleton->translate(t);
}

void MeshWithSkeleton::computeLaplacianParameters() {
   
   /// Compute the Laplacian parameters
   cout<<"Compute Laplacian parameters ... beginnnnnnnnnnnnnnnnn"<<endl;
   
   Vertex_iterator vi = this->vertices_begin();
   while (vi != this->vertices_end()) {
      if (vi->dClosestJoint > largest_dvj) largest_dvj = vi->dClosestJoint;
      ++vi;
   }
   
   Number lfscalek = lfalpha*largest_dvj*largest_dvj;
   vi = this->vertices_begin();
   while (vi != this->vertices_end()) {
      
      //~ cout<<"~~~ vi->lfdenom: 1111"<<vi->lfdenom<<endl;
      setVoronoiArea(vi);
      
      //~ cout<<"~~~ vi->lfdenom: 222"<<vi->lfdenom<<endl;
      
      //~ const Point3 &p = getVertexPosition(vi);
      //~ const Point3 &q = skeleton->joints[vi->rigging_group];
      //~ const Vector3 v = p-q;
      //~ Number d2 = v*v;
      
      Number d2 = vi->dClosestJoint;
      
      //~ cout<<"~~~ d2: "<<d2<<endl;
      
      //~ if (d2 > largest_d2vj) largest_d2vj = d2;
      //~ Number b = (lfscalek*vi->getVoronoiArea())/(d2);
      Number b = (lfscalek*vi->sapb)/(d2*d2);
      
      //~ cout<<"~~~ b: "<<b<<" vi->sapb: "<<vi->sapb<<endl;
      
      //~ vi->lfdenom = 1.0/(b + vi->getVoronoiArea()); // Laplacian filter denominator
      vi->lfdenom = 1.0/(b + vi->sapb); // Laplacian filter denominator
      vi->lfkterm = b*vi->lfdenom; // Laplacian filter constant term
      
      //~ cout<<"vi->lfdenom: "<<vi->lfdenom<<endl;
      //~ cout<<"vi->lfkterm: "<<vi->lfkterm<<endl;
      
      ++vi;
   }
   cout<<"largest_dvj... : "<<largest_dvj<<endl;
   cout<<"Compute Laplacian parameters ... end"<<endl;
}

void MeshWithSkeleton::recoverInitData() {
   
   /// Recover the mesh geometry
   Mesh::recoverInitData();
   
   /// Recover the joints geometry
   if (skeleton != 0 and i_skeleton != 0)
      skeleton->joints = i_skeleton->joints;
}

//~ } // end MLS namespace
